﻿using System.Collections.Generic;
using Gameplay.PVE;
using Gameplay.PVE.Data;
using Gameplay.PVE.Utils;
using UnityEngine;


public class RegionAttribute : MonoBehaviour
{
    public int regionId;
    public bool isCanFight;
    public List<Transform> pathList = new List<Transform>();
    [HideInInspector]
    public PveMapRegion region;

    private GameObject battleRangeObj;

    public void Init(int id,PveMapRegion region)
    {
        regionId = id;
        this.region = region;
        isCanFight = region.isCanFight;
        for (int i = 0; i < region.pathList.Count; i++)
        {
            AddPoint(region.pathList[i],i);
        }

        for (int i = 0; i < region.battles.Count; i++)
        {
            var battle = region.battles[i];
            for (int j = 0; j < battle.enemyIds.Count; j++)
            {
                if (battle.enemyHatredDistances.Count <= j)
                {
                    battle.enemyHatredDistances.Add(3);
                }
                AddEnemy(battle.enemyBirthPositions[j],battle.enemyIds[j],battle.enemySpawnType[j],battle.enemySpawnParam[j],battle.enemyHatredDistances[j]);
            }

            for (int j = 0; j < battle.obstacles.Count; j++)
            {
                var obstacle = battle.obstacles[j];
                AddObstacle(obstacle.position,obstacle.rotation,obstacle.obstacleId,obstacle.hp,obstacle.width,obstacle.height,obstacle.spawnType,obstacle.spawnParam,obstacle.breakId);
            }
        }

        InitBattleRange();
    }

    public void Init(int id)
    {
        regionId = id;
        region = new PveMapRegion();
        region.id = id;
        region.pathList = new List<Vector3>();
        InitBattleRange();
    }

    public void InitBattleRange()
    {
        battleRangeObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
        battleRangeObj.name = "BattleArea";
        battleRangeObj.transform.SetParent(transform);
        battleRangeObj.transform.position = region.battlePosition;
        battleRangeObj.transform.localEulerAngles = new Vector3(0,region.battleAreaRotation,0);
        battleRangeObj.transform.localScale = new Vector3(region.battleAreaWidth,0.01f,region.battleAreaHeight);
    }


    public void AddPoint()
    {
        var position = transform.position;
        int index = -1;
        if (pathList.Count > 0)
        {
            index = pathList.Count - 1;
            position = pathList[index].position;
            AddPoint(position, index + 1);
        }
        else
        {
            AddPoint(position, 0);
        }
    }

    private void AddPoint(Vector3 position,int index)
    {
        var point = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        point.name = "Point" + index;
        point.transform.SetParent(transform);
        point.transform.position = position;
        pathList.Add(point.transform);
    }

    public void AddEnemy()
    {
        var position = transform.position;
        AddEnemy(position);
    }

    private void AddEnemy(Vector3 position,int id = 0,int spawnType = 0,float spawnParam = 0,float hatredDistance = 2)
    {
        var enemy = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
        enemy.name = "Enemy";
        enemy.transform.SetParent(transform);
        enemy.transform.position = position;
        var attribute = enemy.AddComponent<EnemyAttributes>();
        attribute.id = id;
        attribute.spawnParam = spawnParam;
        attribute.spawnType = spawnType;
        attribute.stageId = regionId;
        attribute.hatredDistance = hatredDistance;
        attribute.UpdateModel();
    }

    public void AddObstacle()
    {
        var position = transform.position;
        AddObstacle(position,Vector3.zero);
    }

    private void AddObstacle(Vector3 position,Vector3 angle,int obstacleId = 102,int hp = 0,int width = 3,int height = 3,int spawnType = 0,float spawnParam = 0,int breakId = 0)
    {
        var obj = GetObstacleById(obstacleId);
        obj.name = "Obstacle";
        obj.transform.SetParent(transform);
        obj.transform.position = position;
        obj.transform.localEulerAngles = angle;
        var attribute = obj.AddComponent<ObstacleAttributes>();
        attribute.id = obstacleId;
        attribute.stage = regionId;
        attribute.width = width;
        attribute.height = height;
        attribute.spawnParam = spawnParam;
        attribute.spawnType = spawnType;
        attribute.breakId = breakId;
        attribute.hp = hp;
        UpdateBlockPoints(obj.transform, attribute.width, attribute.height);
#if UNITY_EDITOR
        if (hp == 0)
        {
            UnityEditor.Selection.activeGameObject = obj;
        }
#endif
    }

    public void UpdateObstacles()
    {
        var obstaclesInScene = FindObjectsOfType<ObstacleAttributes>();
        for (int i = 0; i < obstaclesInScene.Length; i++)
        {
            var attr = obstaclesInScene[i];
            if (attr.stage != regionId)
            {
                continue;
            }
            AddObstacle(attr.gameObject.transform.position,attr.gameObject.transform.localEulerAngles,attr.id,attr.hp,attr.width,attr.height,attr.spawnType,attr.spawnParam,attr.breakId);
            GameObject.DestroyImmediate(attr.gameObject);
        }
    }

    private GameObject GetObstacleById(int id)
    {
#if UNITY_EDITOR
        var path = PveResourceManager.GetHeroPath(id) + ".prefab";
        var obj = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
        if (obj == null)
        {
            return new GameObject();
        }
        return Instantiate(obj);
#endif
        return new GameObject();
    }

    public void SaveRegion()
    {
        region.pathList.Clear();
        for (int i = 0; i < pathList.Count; i++)
        {
            region.pathList.Add(pathList[i].transform.position);
        }
        region.position = transform.position;

        region.battlePosition = battleRangeObj.transform.position;
        region.battleAreaRotation = battleRangeObj.transform.localEulerAngles.y;
        region.battleAreaWidth = battleRangeObj.transform.localScale.x;
        region.battleAreaHeight = battleRangeObj.transform.localScale.z;
        region.isCanFight = isCanFight;


        var enemyInScene = FindObjectsOfType<EnemyAttributes>();
        List<RpgLevelData.RpgStage> list = new List<RpgLevelData.RpgStage>();

        var enemyPositions = new List<Vector3>();
        var enemyIds = new List<int>();
        var enemySpawnParam = new List<float>();
        var enemySpawnType = new List<int>();
        var enemyHatredDistances = new List<float>();
        for (int i = 0; i < enemyInScene.Length; i++)
        {
            var enemyAttribute = enemyInScene[i];
            var stage = enemyAttribute.stageId;
            if (stage == regionId)
            {
                enemyPositions.Add(enemyAttribute.transform.position);
                enemyIds.Add(enemyAttribute.id);
                enemySpawnParam.Add(enemyAttribute.spawnParam);
                enemySpawnType.Add(enemyAttribute.spawnType);
                enemyHatredDistances.Add(enemyAttribute.hatredDistance);
            }
        }
        var obstaclesInScene = FindObjectsOfType<ObstacleAttributes>();
        var obstacles = new List<RpgLevelData.RpgObstacle>();
        for (int i = 0; i < obstaclesInScene.Length; i++)
        {
            var attribute = obstaclesInScene[i].GetComponent<ObstacleAttributes>();
            var obstacleId = attribute.id;
            var stage = attribute.stage;
            if (stage == regionId)
            {
                var playerPoints = new List<Vector3>();
                var enemyPoints = new List<Vector3>();
                for (int j = 0; j < attribute.playerHidePoints.Count; j++)
                {
                    if (attribute.playerHidePoints[j] == null)
                    {
                        continue;
                    }

                    playerPoints.Add(attribute.playerHidePoints[j].transform.position);
                }

                for (int j = 0; j < attribute.enemyHidePoints.Count; j++)
                {
                    if (attribute.enemyHidePoints[j] == null)
                    {
                        continue;
                    }

                    enemyPoints.Add(attribute.enemyHidePoints[j].transform.position);
                }

                obstacles.Add(new RpgLevelData.RpgObstacle
                {
                    id = i,
                    hp = attribute.hp,
                    playerHidePoints = playerPoints,
                    enemyHidePoints = enemyPoints,
                    obstacleId = obstacleId,
                    position = obstaclesInScene[i].transform.position,
                    rotation = obstaclesInScene[i].transform.localEulerAngles,
                    scale = obstaclesInScene[i].transform.localScale,
                    height = attribute.height,
                    width = attribute.width,
                    breakId = attribute.breakId,
                    spawnParam = attribute.spawnParam,
                    spawnType = attribute.spawnType,
                });
            }
        }
        list.Add(new RpgLevelData.RpgStage
        {
            stageType = 0,
            stageId = regionId,
            enemyBirthPositions = enemyPositions,
            enemySpawnParam = enemySpawnParam,
            enemySpawnType = enemySpawnType,
            enemyHatredDistances = enemyHatredDistances,
            enemyIds = enemyIds,
            obstacles = obstacles,
        });
        region.battles = list;
    }

    private void UpdateBlockPoints(Transform parent,int width,int height)
    {
        for (int i = 0; i < parent.childCount; i++)
        {
            var child = parent.GetChild(i);
            if (child.gameObject.name.Contains("Astar"))
            {
                GameObject.DestroyImmediate(child.gameObject);
                i--;
            }
        }


        Vector3[] corners = new Vector3[4];
        float gridW = AStarMgr.gridSize.x;
        float gridH = AStarMgr.gridSize.y;

        corners[0] = new Vector3(-width / 2f * gridW,0,-height / 2f * gridH);
        corners[1] = new Vector3(-width / 2f * gridW,0,height / 2f * gridH);
        corners[2] = new Vector3(width / 2f * gridW,0,height / 2f * gridH);
        corners[3] = new Vector3(width / 2f * gridW,0,-height / 2f * gridH);

        float parentY = parent.localEulerAngles.y;
        for (int i = 0; i < corners.Length; i++)
        {
            corners[i] = Quaternion.AngleAxis(parentY, Vector3.up) * corners[i];
        }

        var position = parent.position;
        int halfWidth = Mathf.CeilToInt(width / 2f);
        int halfHeight = Mathf.CeilToInt(height / 2f);
        float midX = Mathf.RoundToInt(position.x / gridW) * gridW;
        float midZ = Mathf.RoundToInt(position.z / gridH) * gridH;
        int region = halfWidth + halfHeight;

        Vector2[] cornersV2 = new Vector2[4];
        for (int i = 0; i < 4; i++)
        {
            cornersV2[i] = new Vector2(corners[i].x,corners[i].z);
        }
        for (float i = -region; i <= region; i ++)
        {
            for (float j = -region; j <= region; j ++)
            {
                bool isBlock = true;
                var blockPos = new Vector2(i * gridW,j * gridH);
                for (int k = 0; k < 4; k++)
                {
                    var cornerStart = cornersV2[k % 4];
                    var cornerEnd = cornersV2[(k + 1) % 4];
                    if (PveUtils.GetIntersection(cornerStart, cornerEnd, Vector2.zero, blockPos) != Vector2.zero)
                    {
                        isBlock = false;
                        break;
                    }
                }

                if (isBlock)
                {
                    var block = GameObject.CreatePrimitive(PrimitiveType.Plane);
                    block.name = "AstarBlock";
                    block.transform.localEulerAngles = Vector3.zero;
                    block.transform.localScale = new Vector3(gridW,1,gridH) / 10f *0.9f;
                    block.transform.parent = parent;
                    block.transform.position = new Vector3(i * gridW + midX,0.1f,j * gridH + midZ);
                    block.GetComponent<Renderer>().material.color = Color.black;
                }
            }
        }
    }
}
